package org.ruoyi.ppt.service;

/**
 *
 *
 * @author anran
 * @date 2025/9/30/周二 15:57
 */
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.ruoyi.common.oss.core.OssClient;
import org.ruoyi.common.oss.entity.UploadResult;
import org.ruoyi.common.oss.factory.OssFactory;
import org.ruoyi.ppt.domain.PptImage;
import org.ruoyi.ppt.domain.PptResult;
import org.ruoyi.ppt.mapper.PptImageMapper;
import org.ruoyi.ppt.mapper.PptResultMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.InputStream;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class PptImportService {

    private final PptResultMapper pptResultMapper;

    private final PptImageMapper pptImageMapper;

    /**
     * 入参就是  /api/ppt/ppt-result 返回的完整 JSON 字符串
     */
    @Transactional(rollbackFor = Exception.class)
    public void importFromJson(String fullJson) {
        // 1. 取出 data
        JSONObject data = JSON.parseObject(fullJson).getJSONObject("data");

        // 2. 构造主表实体
        PptResult pr = constructPptResult(data);

        // 3. 写入主表（存在则先删除，再插入，可按需改成 update）
        deleteAndInsertPptResult(pr);

        // 4. 构造子表列表
        List<PptImage> images = constructPptImages(data, pr.getId());

        // 5. 清旧图 & 插新图
        deleteAndInsertPptImages(pr.getId(), images);
    }

    /**
     * 批量入库优化版本 - 一次IO完成所有数据入库
     */
    @Transactional(rollbackFor = Exception.class)
    public void importFromJsonBatch(String fullJson) {
        // 1. 取出 data
        JSONObject data = JSON.parseObject(fullJson).getJSONObject("data");

        // 2. 构造主表实体
        PptResult pr = constructPptResult(data);

        // 3. 准备子表数据列表
        List<PptImage> images = constructPptImages(data, pr.getId());

        // 4.1 删除旧数据
        LambdaQueryWrapper<PptResult> mainWrapper = new LambdaQueryWrapper<>();
        mainWrapper.eq(PptResult::getId, pr.getId());
        pptResultMapper.delete(mainWrapper);

        LambdaQueryWrapper<PptImage> imageWrapper = new LambdaQueryWrapper<>();
        imageWrapper.eq(PptImage::getPptId, pr.getId());
        pptImageMapper.delete(imageWrapper);

        // 4.2 批量插入新数据
        pptResultMapper.insert(pr);

        if (!images.isEmpty()) {
            // 使用MyBatis-Plus的批量插入方法，一次IO完成所有图片插入
            pptImageMapper.insertBatchSomeColumn(images);
        }
    }

    /**
     * 构造PptResult对象
     */
    private PptResult constructPptResult(JSONObject data) {
        PptResult pr = new PptResult();
        pr.setId(data.getString("id"));
        pr.setPptTitle(data.getString("ppt_title"));
        pr.setIntroduce(data.getString("introduce"));
        pr.setPageCount(data.getIntValue("page_count"));
        pr.setProgress(data.getIntValue("progress"));
        pr.setStatus(data.getIntValue("status"));
        pr.setNoteStatus(data.getIntValue("note_status"));
        pr.setStateDescription(data.getString("state_description"));
        pr.setProcessUrl(data.getString("process_url"));
        pr.setPreviewUrl(data.getString("preview_url"));

        JSONArray arr = data.getJSONArray("images_url");
        if (arr != null && !arr.isEmpty()) {
            pr.setPreviewImage(transferToMinIO(arr.getJSONObject(0).getString("url")));
        }

        // 时间字段：字符串 -> LocalDateTime
        pr.setFirstImageUpAt(parseLocalDateTime(data.getString("first_image_up_at")));
        pr.setCreatedAt(parseLocalDateTime(data.getString("created_at")));
        pr.setUpdatedAt(parseLocalDateTime(data.getString("updated_at")));

        return pr;
    }

    /**
     * 构造PptImage对象列表
     */
    private List<PptImage> constructPptImages(JSONObject data, String pptId) {
        JSONArray arr = data.getJSONArray("images_url");
        List<PptImage> images = new ArrayList<>(arr.size());

        for (int i = 0; i < arr.size(); i++) {
            JSONObject o = arr.getJSONObject(i);
            PptImage img = new PptImage();
            img.setPptId(pptId);
            img.setImageUrl(transferToMinIO(o.getString("url")));
            img.setUploadTime(o.getIntValue("time"));
            images.add(img);
        }

        return images;
    }

    /**
     * 删除旧PptResult并插入新PptResult
     */
    private void deleteAndInsertPptResult(PptResult pr) {
        LambdaQueryWrapper<PptResult> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PptResult::getId, pr.getId());
        pptResultMapper.delete(wrapper);
        pptResultMapper.insert(pr);
    }

    /**
     * 删除旧PptImage并插入新PptImage列表
     */
    private void deleteAndInsertPptImages(String pptId, List<PptImage> images) {
        LambdaQueryWrapper<PptImage> imgWrapper = new LambdaQueryWrapper<>();
        imgWrapper.eq(PptImage::getPptId, pptId);
        pptImageMapper.delete(imgWrapper);

        images.forEach(pptImageMapper::insert);
    }

    /**
     * 把外网图片拉下来并转存 MinIO，返回 MinIO 地址
     */
    private String transferToMinIO(String externalUrl) {
        if (externalUrl == null || externalUrl.isEmpty()) {
            log.error("externalUrl is null or empty");
            return externalUrl;
        }

        try (InputStream in = new URL(externalUrl).openStream()) {
            String suffix = externalUrl.substring(externalUrl.lastIndexOf('.'));   // .png

            OssClient storage = OssFactory.instance();
            UploadResult result = storage.uploadSuffix(in, suffix, "image/png");
            return result.getUrl();          // MinIO 地址
        } catch (Exception e) {
            log.error("转存 MinIO 失败，externalUrl={}", externalUrl, e);
            // 容错：转存失败就继续用原地址，避免整单回滚
            return externalUrl;
        }
    }

    /**
     * 将字符串解析为LocalDateTime
     */
    private LocalDateTime parseLocalDateTime(String dateTimeStr) {
        if (dateTimeStr == null || dateTimeStr.isEmpty()) {
            log.error("dateTimeStr is null or empty");
            return null;
        }

        try {
            return LocalDateTime.parse(dateTimeStr.replace(' ', 'T'));
        } catch (Exception e) {
            log.error("解析时间字段失败，dateTimeStr={}", dateTimeStr, e);
            return null;
        }
    }
}
